/*
Author ShawJay
使用时，建议将counter改为pkg目录下文件，供其他模块引用
即author只可以通过new相应函数使用,保证内部不可见,防止误用函数导致出错

目前在初始化的时候,如果遇到相同key的,重置时间的问题
通过冗余字段,延迟删除技术实现

*/

package counter

import (
	"context"
	"fmt"
	"sync"
	"time"
)

type point struct {
	incr       int
	cancelFlag bool
}

type counter2 struct {
	ctx    context.Context
	exit   context.CancelFunc
	cancel chan string
	m      map[string]*point
	mutex  sync.Mutex
	clear  chan func()
}

func NewCounter2() *counter2 {
	ctx := context.Background()
	ctx, cancel := context.WithCancel(ctx)
	c := counter2{
		m:      make(map[string]*point),
		cancel: make(chan string),
		clear:  make(chan func()),
		ctx:    ctx,
		exit:   cancel,
	}
	go func() {
		c.init()
	}()
	return &c
}

func (c *counter2) Exit() {
	c.exit()
	fmt.Println("counter2 will exit after 1s")
	time.Sleep(1 * time.Second)
}

func (c *counter2) init() {
	for {
		select {
		case clear := <-c.clear:
			{
				go func() {
					clear()
				}()
			}
		case cancelKey := <-c.cancel:
			{
				c.mutex.Lock()
				fmt.Println("cancel ", cancelKey)
				c.m[cancelKey].cancelFlag = true
				c.mutex.Unlock()
			}
		}
	}
}

func (c *counter2) Query(key string) int {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	if _, ok := c.m[key]; ok {
		if c.m[key].cancelFlag == true {
			return -1
		} else {
			return c.m[key].incr
		}
	}
	return -1
}

func (c *counter2) Cnt(key string, cnt int) {
	if c.Query(key) != -1 {
		c.mutex.Lock()
		defer c.mutex.Unlock()
		c.m[key].incr += cnt
	} else {
		fmt.Println("key not exist or not init")
	}
}

func (c *counter2) InitIncr(duration int, key string) {
	if c.Query(key) != -1 {
		fmt.Println("key has been existed, old key will be delete")
		c.cancel <- key
	}
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.m[key] = &point{
		incr:       0,
		cancelFlag: false,
	}
	clear := func() {
	loop:
		for {
			select {
			case <-time.After(time.Duration(duration) * time.Millisecond):
				{
					c.mutex.Lock()
					fmt.Println(key, c.m[key])
					if c.m[key].cancelFlag == true {
						delete(c.m, key)
						c.mutex.Unlock()
						break loop
					}
					c.m[key].incr = 0
					c.mutex.Unlock()
				}
			case <-c.ctx.Done():
				{
					break loop
				}
			}
		}
		fmt.Println("exist clear")
	}
	c.clear <- clear
}
